import numpy as np
import random


def get_packer_action(machine, job_slot):
    align_score = 0
    act = len(job_slot.slot)  # if no action available, hold

    for i in range(len(job_slot.slot)):
        new_job = job_slot.slot[i]
        if new_job is not None:  # there is a pending job

            avbl_res = machine.avbl_slot[:new_job.len, :]
            res_left = avbl_res - new_job.res_vec

            if np.all(res_left[:] >= 0):  # enough resource to allocate

                tmp_align_score = avbl_res[0, :].dot(new_job.res_vec)

                if tmp_align_score > align_score:
                    align_score = tmp_align_score
                    act = i
    return act


"""
模拟退火算法
"""

# def get_packer_action(machine, job_slot):
#     return simulated_annealing(machine, job_slot)
#
#
# def simulated_annealing(machine, job_slot, initial_temperature=100, cooling_rate=0.60, max_iterations=100):
#     best_action = len(job_slot.slot)  # Initialize the best action
#     best_score = calculate_score(machine, job_slot, best_action)
#     current_action = best_action
#     current_score = best_score
#     temperature = initial_temperature
#
#     for _ in range(max_iterations):
#         neighbor_action = generate_neighbor(current_action, len(job_slot.slot))
#         neighbor_score = calculate_score(machine, job_slot, neighbor_action)
#         energy_change = neighbor_score - current_score
#
#         if energy_change < 0 or np.random.rand() < np.exp(-energy_change / temperature):
#             current_action = neighbor_action
#             current_score = neighbor_score
#
#         if current_score > best_score:
#             best_action = current_action
#             best_score = current_score
#
#         temperature *= cooling_rate
#
#     return best_action
#
#
# def generate_neighbor(current_solution, num_actions):
#     neighbor_solution = current_solution
#     while neighbor_solution == current_solution:
#         neighbor_solution = np.random.randint(0, num_actions)  # 随机选择一个动作
#     return neighbor_solution
#
#
# def calculate_score(machine, job_slot, action):
#     new_job = job_slot.slot[action-1]
#
#     if new_job is None:
#         return 0.0  # 如果动作是保持，分数为0
#
#     avbl_res = machine.avbl_slot[:new_job.len, :]
#     res_left = avbl_res - new_job.res_vec
#
#     if np.all(res_left[:] >= 0):
#         # 如果有足够的资源可供分配，分数基于可用资源的总和
#         score = np.sum(avbl_res)
#     else:
#         # 如果资源不足，分数为负无穷表示不可行的动作
#         score = float('-inf')
#
#     return score
#

# 示例用法：
# action = get_packer_action(machine, job_slot)


"""
遗传算法
"""


def get_genetic_algorithm_action(machine, job_slot, population_size=100, generations=100):
    def fitness(individual):
        # 计算适应度分数
        sjf_score = 0
        act = len(job_slot.slot)  # 如果没有操作可用，则保持不变

        for i in range(len(job_slot.slot)):
            new_job = individual[i]
            if new_job is not None:  # 有待处理的作业

                avbl_res = machine.avbl_slot[:new_job.len, :]
                res_left = avbl_res - new_job.res_vec

                if np.all(res_left[:] >= 0):  # 足够的资源来分配

                    tmp_sjf_score = 1 / float(new_job.len)

                    if tmp_sjf_score > sjf_score:
                        sjf_score = tmp_sjf_score
                        # act = i

        return sjf_score  # 这里返回SJF分数作为适应度分数

    def select(population):
        # 选择操作，可以根据适应度分数选择父代个体
        return random.choice(population)



    def crossover(parent1, parent2):
        # 交叉操作，这里示例中没有具体的交叉逻辑，您可以根据需要实现

        return random.choice([parent1, parent2])

    def mutate(individual):
        # 变异操作

        return individual  # 不进行变异，保持个体不变


    # 初始化一个随机种群
    population = [random.sample(job_slot.slot, len(job_slot.slot)) for _ in range(population_size)]

    # 进行遗传算法的优化
    for _ in range(generations):
        # 选择和繁殖下一代
        new_population = []
        for _ in range(population_size):
            parent1 = select(population)
            parent2 = select(population)
            child = crossover(parent1, parent2)
            child = mutate(child)
            new_population.append(child)
        population = new_population

    # 根据适应度函数选择最佳个体的索引
    best_individual_index = max(range(len(population)), key=lambda i: fitness(population[i]))
    act = best_individual_index

    return act


# 获取遗传算法中的Packer-SJF动作选择
def get_genetic_algorithm_packer_action(machine, job_slot, knob):  # 可以根据需要调整
    combined_score = 0
    act = len(job_slot.slot)  # 如果没有操作可用，则保持不变

    for i in range(len(job_slot.slot)):
        new_job = job_slot.slot[i]
        if new_job is not None:  # 有待处理的作业

            avbl_res = machine.avbl_slot[:new_job.len, :]
            res_left = avbl_res - new_job.res_vec

            if np.all(res_left[:] >= 0):  # 足够的资源来分配

                tmp_align_score = avbl_res[0, :].dot(new_job.res_vec)
                tmp_sjf_score = 1 / float(new_job.len)

                tmp_combined_score = knob * tmp_align_score + (1 - knob) * tmp_sjf_score

                if tmp_combined_score > combined_score:
                    combined_score = tmp_combined_score
                    act = i

    return act


